#include <PopupDefaultLogger.hpp>
#include <string>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/rand.h>
#include <openssl/blowfish.h>
#include <openssl/err.h>
#include <string.h>

using namespace Popup;
using namespace std;

#define NULL_IVEC { 0, 0, 0, 0, 0, 0, 0, 0 }

static PopupDefaultLogger logger;

int main(int argc, char *argv[])
{
  unsigned char *rawpubkey = NULL;
  unsigned char *rawpubkey2 = NULL;

  const char *salt = "Iu{àç)àðkzokfsesmdfçsà=q=zerç;'zM%zpo)0a82À=X_&àçuca,éM'c;qie(c,vmey(çyw<s]+&~éé";
  const char *data = 
    "bonjour, comment vas-tu? Ben moi ca va pas mal du tout, disons qu'on fait aller quoi!! Bon en tout cas, je te souhaite plein de bonnes choses!! bonjour, comment vas-tu? Ben moi ca va pas mal du tout, disons qu'on fait aller quoi!! Bon en tout cas, je te souhaite plein de bonnes choses!! bonjour, comment vas-tu? Ben moi ca va pas mal du tout, disons qu'on fait aller quoi!! Bon en tout cas, je te souhaite plein de bonnes choses!! bonjour, comment vas-tu? Ben moi ca va pas mal du tout, disons qu'on fait aller quoi!! Bon en tout cas, je te souhaite plein de bonnes choses!! bonjour, comment vas-tu? Ben moi ca va pas mal du tout, disons qu'on fait aller quoi!! Bon en tout cas, je te souhaite plein de bonnes choses!!";
  int datasize = strlen(data)+1;
  char error[256];
  
  BIGNUM *oBigNbr = BN_new();
  RSA *key = RSA_new();

  RAND_seed(salt, strlen(salt));

  //------------- KeyGen

  BN_set_word(oBigNbr, RSA_F4);
  RSA_generate_key_ex(key, 1024, oBigNbr, NULL);
  
  // Write public key to a buffer
  int size = i2d_RSAPublicKey(key, &rawpubkey);

  for (int i = 0; i < size; i+=2) {
    if (i % 64 == 0) {
      printf("\n");
    }
    printf("%02hx", rawpubkey[i]);
  }
  
  // Load the public key from the buffer
  unsigned char *pkey = new unsigned char(1024);// (unsigned char*) malloc(1024);//new unsigned char(1024);

  memcpy((unsigned char*) pkey, rawpubkey, size);

  // Read key somewhere else
  RSA *key2 = d2i_RSAPublicKey(NULL, (const unsigned char**) &pkey, size);

  //free(pkey);

  size = i2d_RSAPublicKey(key2, &rawpubkey2);
  for (int i = 0; i < size; i+=2) {
    if (i % 64 == 0) {
      printf("\n");
    }
    printf("%02hx", rawpubkey2[i]);
  }

  //------------- Codage RSA

  printf("\n");
  printf("rsasize1 = %d\n", RSA_size(key));
  printf("rsasize2 = %d\n", RSA_size(key2));

  int blocsize = RSA_size(key);
  int buffsize = blocsize * (datasize / (2*blocsize) + 1);
  printf("ALlocated=%d\n", buffsize);
  unsigned char buffer[1024]; // = (unsigned char*) malloc(buffsize);
  int encodedsize = 0;
  int remainingSize = datasize;
  for (size_t offsetin = 0, offsetout = 0;
       offsetin < datasize; 
       offsetin += 100, offsetout += blocsize) {
    int size = 0;
    if (remainingSize < 100) {
      size = remainingSize;
    } else {
      size = 100;
    }
    remainingSize -= size;
    encodedsize += RSA_public_encrypt(size,
				      (const unsigned char*) &data[offsetin], 
				      (unsigned char*) &buffer[offsetout], key2,
				      RSA_PKCS1_PADDING);
    if (encodedsize == -1) {
      ERR_error_string(ERR_get_error(), &error[0]);
    }
  }
  
  printf("Encoded Message = \n");
  for (size_t i = 0; i < encodedsize; i+=2) {
    if (i % 64 == 0) {
      printf("\n");
    }
    printf("%02hx", buffer[i]);
  }
  
  //--------- Decodage RSA 

  unsigned char decodedMessage[1024]; // = (unsigned char*) malloc(1024);
  
  int decsize = 0;
  int totalsize = 0;
  for (size_t offsetin = 0, offsetout = 0;
       offsetin < encodedsize;
       offsetin += RSA_size(key), offsetout = totalsize) {
    decsize = RSA_private_decrypt(RSA_size(key), (const unsigned char*)&buffer[offsetin], 
				  &decodedMessage[offsetout], key,
				  RSA_PKCS1_PADDING);
    totalsize += decsize;
  }
  
  
  printf("\nTotalsize decoded = %d\n", totalsize);

  printf("Decoded message = \n%s\n", (char*) decodedMessage);

  //------------ Encodage blowfish

  static char bfkeyascii[128] = "abcdefghijklmnopqrstuvwxyz1234567abcdefghijklmnopqrstuvwxyz1234567abcdefghijklmnopqrstuvwxyz1234567abcdefghijklmnopqrstuvwxyz16";
  
  BF_KEY bfkey;
  int alignedDataSize = sizeof(BF_LONG) * ((datasize+sizeof(BF_LONG)-1)/sizeof(BF_LONG));
  unsigned char *datacopy = (unsigned char*) malloc(datasize);
  //  unsigned char *datadec = (unsigned char*) malloc(datasize);
  
  printf("alignedsize=%d\n", alignedDataSize);
  memset(datacopy, 0, alignedDataSize);
  memcpy(datacopy, data, datasize);
  //  memset(datadec, 0, datasize);
  
  printf("AVANT ENCRYPT\n");
  for (size_t i = 0; i < datasize; i+=2) {
    if (i % 64 == 0) {
      printf("\n");
    }
    printf("%02hx", datacopy[i]);
  }

  BF_set_key(&bfkey, 128, (unsigned char *)bfkeyascii);

  unsigned char ivec[8] = NULL_IVEC;
  BF_cbc_encrypt((const unsigned char*)datacopy, datacopy, datasize, (const BF_KEY*)&bfkey, ivec, BF_ENCRYPT);
  //for (int i = 0; i < datasize; i += sizeof(BF_LONG)) {
  //  BF_encrypt((BF_LONG*)&datacopy[i], &bfkey);
  //}
  printf("\nAPRES ENCRYPT\n");
  for (size_t i = 0; i < datasize; i+=2) {
    if (i % 64 == 0) {
      printf("\n");
    }
    printf("%02hx", datacopy[i]);
  }

  //------------ Decodage blowfish
  unsigned char ivec2[8] = NULL_IVEC;
  BF_cbc_encrypt(datacopy, datacopy, datasize, (const BF_KEY*)&bfkey, ivec2, BF_DECRYPT);

  //for (int i = 0; i < datasize; i += sizeof(BF_LONG)) {
  //  BF_decrypt((BF_LONG*)&datacopy[i], &bfkey);
  //}

  printf("APRES DECRYPT\n");
  for (size_t i = 0; i < datasize; i+=2) {
    if (i % 64 == 0) {
      printf("\n");
    }
    printf("%02hx", datacopy[i]);
  }

  printf("%s\n", datacopy);

  BN_free(oBigNbr);
  RSA_free(key); 
  //RSA_free(key2); 
  
  return 0;
}
